package com.gitee.wsl.doc.prosemirror.model.node.ext

import com.gitee.wsl.doc.prosemirror.model.content.ContentMatch
import com.gitee.wsl.doc.prosemirror.model.node.Node


// Test whether the blocks before and after a given position can be joined.
fun Node.canJoin(pos: Int): Boolean {
    val doc = this
    val thisPos = doc.resolve(pos)
    val index = thisPos.index()
    return joinable(thisPos.nodeBefore, thisPos.nodeAfter) && thisPos.parent.canReplace(index, index + 1)
}

fun Node.canJoin(other: Node): Boolean = joinable(this,other)

fun canAppendWithSubstitutedLinebreaks(a: Node, b: Node): Boolean {
    if (b.content.size == 0) {
        return a.type.compatibleContent(b.type)
    }
    var match: ContentMatch? = a.contentMatchAt(a.childCount)
    val linebreakReplacement = a.type.schema.linebreakReplacement
    for (i in 0 until b.childCount) {
        val child = b.child(i)
        val type = if (child.type == linebreakReplacement) a.type.schema.nodeType("text") else child.type
        match = match?.matchType(type)
        if (match == null) return false
        if (!a.type.allowsMarks(child.marks)) return false
    }
    return match?.validEnd ?: false
}

fun joinable(a: Node?, b: Node?) = a != null && b != null && !a.isLeaf && canAppendWithSubstitutedLinebreaks(a, b)


// Find an ancestor of the given position that can be joined to the block before (or after if `dir`
// is positive). Returns the joinable point, if any.
fun Node.joinPoint(pos: Int, dir: Int = -1): Int? {
    val doc = this
    var pos = pos
    val thisPos = doc.resolve(pos)
    var d = thisPos.depth
    while (true) {
        var before: Node?
        val after: Node?
        var index = thisPos.index(d)
        if (d == thisPos.depth) {
            before = thisPos.nodeBefore
            after = thisPos.nodeAfter
        } else if (dir > 0) {
            before = thisPos.node(d + 1)
            index++
            after = thisPos.node(d).maybeChild(index)
        } else {
            before = thisPos.node(d).maybeChild(index - 1)
            after = thisPos.node(d + 1)
        }
        if (before != null && !before.isTextblock && joinable(before, after) &&
            thisPos.node(d).canReplace(index, index + 1)
        ) {
            return pos
        }
        if (d == 0) break
        pos = if (dir < 0) thisPos.before(d) else thisPos.after(d)
        d--
    }
    return null
}